/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - RMI Client, FileChooser and WebDAV
 */
package de.ipk_gatersleben.bit.bi.edal.webdav.wrapper;

import io.milton.http.AuthenticationHandler;
import io.milton.http.Filter;
import io.milton.http.HttpManager;
import io.milton.http.ResourceFactory;
import io.milton.http.webdav.WebDavResponseHandler;
import io.milton.servlet.Config;
import io.milton.servlet.Initable;
import io.milton.servlet.MiltonConfigurator;

import java.util.*;
import javax.servlet.ServletException;

import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientDataManager;

/**
 * 
 * Provide init-params to the filter or servlet to configure it:
 * resource.factory.class - the class of your resource factory
 * response.handler.class - specify if you want a different response handler
 * authenticationHandlers - a list of class names for the authentication
 * handlers filter_X - define an ordered list of milton filters, where the name
 * is in the form filter_1, filter_2, etc and the value is the name of the
 * filter class
 * 
 * @author Jinbo
 */
public class EdalMiltonConfigurator implements MiltonConfigurator {

	protected EdalHttpManagerBuilder builder = new EdalHttpManagerBuilder();

	protected List<Initable> initables;

	protected HttpManager httpManager;

	@Override
	public HttpManager configure(Config config) throws ServletException {

		ClientDataManager.logger.info("Listing all config parameters:");
		for (String s : config.getInitParameterNames()) {
			ClientDataManager.logger.info(" " + s + " = "
					+ config.getInitParameter(s));
		}

		String authHandlers = config.getInitParameter("authenticationHandlers");
		if (authHandlers != null) {
			initAuthHandlers(authHandlers);
		}
		String resourceFactoryClassName = config
				.getInitParameter("resource.factory.class");
		if (resourceFactoryClassName != null) {
			ResourceFactory rf = instantiate(resourceFactoryClassName);
			builder.setMainResourceFactory(rf);
		} else {
			ClientDataManager.logger
					.warn("No custom ResourceFactory class name provided in resource.factory.class");
		}
		String responseHandlerClassName = config
				.getInitParameter("response.handler.class");
		if (responseHandlerClassName != null) {
			WebDavResponseHandler davResponseHandler = instantiate(responseHandlerClassName);
			builder.setWebdavResponseHandler(davResponseHandler);
		}
		List<Filter> filters = null;
		List<String> params = config.getInitParameterNames();
		for (String paramName : params) {
			if (paramName.startsWith("filter_")) {
				String filterClass = config.getInitParameter(paramName);
				Filter f = instantiate(filterClass);
				if (filters == null) {
					filters = new ArrayList<Filter>();
				}
				filters.add(f);
			}
		}
		if (filters != null) {
			builder.setFilters(filters);
		}
		build();
		initables = new ArrayList<Initable>();

		checkAddInitable(initables, builder.getAuthenticationHandlers());
		checkAddInitable(initables, builder.getMainResourceFactory());
		checkAddInitable(initables, builder.getWebdavResponseHandler());
		checkAddInitable(initables, builder.getFilters());

		for (Initable i : initables) {
			i.init(config, httpManager);
		}
		return httpManager;
	}

	/**
	 * Actually builds the httpManager. Can be overridden by subclasses
	 */
	protected void build() {
		httpManager = builder.buildHttpManager();
	}

	@Override
	public void shutdown() {
		httpManager.shutdown();
		for (Initable i : initables) {
			i.destroy(httpManager);
		}
	}

	private void initAuthHandlers(String classNames) throws ServletException {
		List<String> authHandlers = loadAuthHandlersIfAny(classNames);
		if (authHandlers == null) {
			return;
		}
		List<AuthenticationHandler> list = new ArrayList<AuthenticationHandler>();
		for (String authHandlerClassName : authHandlers) {
			Object o = instantiate(authHandlerClassName);
			if (o instanceof AuthenticationHandler) {
				AuthenticationHandler auth = (AuthenticationHandler) o;
				list.add(auth);
			} else {
				throw new ServletException("Class: " + authHandlerClassName
						+ " is not a: "
						+ AuthenticationHandler.class.getCanonicalName());
			}
		}
		builder.setAuthenticationHandlers(list);
	}

	public static <T> T instantiate(String className) throws ServletException {
		try {
			Class c = Class.forName(className);
			T rf = (T) c.newInstance();
			return rf;
		} catch (Throwable ex) {
			throw new ServletException("Failed to instantiate: " + className,
					ex);
		}
	}

	private List<String> loadAuthHandlersIfAny(String initParameter) {
		if (initParameter == null) {
			return null;
		}
		String[] arr = initParameter.split(",");
		List<String> list = new ArrayList<String>();
		for (String s : arr) {
			s = s.trim();
			if (s.length() > 0) {
				list.add(s);
			}
		}
		return list;
	}

	private void checkAddInitable(List<Initable> initables, Object o) {
		if (o instanceof Initable) {
			initables.add((Initable) o);
		} else if (o instanceof List) {
			for (Object o2 : (List) o) {
				checkAddInitable(initables, o2);
			}
		}
	}
}
